/*
 * Copyrignt (c) xuzeshui.com. All Rights Reserved.
 * Author: Zeshui Xu<i@xuzeshui.com>
 * Created Time: 2015-09-29 21:12:16
 * Last Modified: 2017-03-31 19:45:30
 * File Name: models/token.go
 * Description:
 */
package account

import (
	"fmt"

	"doubimeizhi.com/utility"
	"passport/models"

	log "github.com/cihub/seelog"
	"github.com/garyburd/redigo/redis"
)

/*
	ToeknService
*/
type AccessTokenService struct{}

/*
	AccessToken的存储redis key
*/
func (p *AccessTokenService) GetAccessTokenKey(token string) string {
	return fmt.Sprintf("t:%s", token)
}

/*
	userid对应的各akid下的token数据
*/
func (p *AccessTokenService) getUseridAKKey(userid string) string {
	return fmt.Sprintf("ut:%s", userid)
}

/*
	获取用户所有的应用下ak值
*/
func (p *AccessTokenService) AllUserAK(userid string) map[string]string {
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	if reply, err := redis.StringMap(conn.Do("hgetall", p.getUseridAKKey(userid))); err == nil {
		return reply
	} else {
		log.Errorf("hgetall %s error: %s", p.getUseridAKKey(userid), err.Error())
	}
	return nil
}

/*
	获取用户指定akid下的accessToken
*/
func (p *AccessTokenService) AccessTokenByUserAKId(userid string, akid int) string {
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	if reply, err := redis.String(conn.Do("hget", p.getUseridAKKey(userid), akid)); err == nil {
		return reply
	}
	return ""
}

/*
	删除失效用户的accessToken
*/
func (p *AccessTokenService) ExpireUserAccessToken(userid string) {
	aks := p.AllUserAK(userid)
	if aks == nil {
		return
	}
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	for _, ak := range aks {
		conn.Send("del", p.GetAccessTokenKey(ak))
	}
	conn.Do("del", p.getUseridAKKey(userid))
}

/*
	记录保存userid和access_token的关系
*/
func (p *AccessTokenService) recordAccessToken(accessToken, userid string, akid int) error {
	tokenKey := p.GetAccessTokenKey(accessToken)
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	conn.Send("set", tokenKey, userid)
	expireAKSecond := models.GetAccessTokenExpireSecond()
	if akid != 0 {
		if oldAccessToken := p.AccessTokenByUserAKId(userid, akid); oldAccessToken != "" {
			conn.Send("del", p.GetAccessTokenKey(oldAccessToken))
		}
		conn.Send("hmset", p.getUseridAKKey(userid), akid, accessToken)
	}
	_, err := conn.Do("expire", tokenKey, expireAKSecond)
	return err
}

/*
	通过access_token获取对应的userid
	返回参数二表示access_token是否合法,当且仅当合法(true)时userid才有
*/
func (p *AccessTokenService) GetUseridByAccessToken(accessToken string) (string, int, bool) {
	if "" == accessToken || len(accessToken) < 22 {
		log.Warnf("params for accessToken is invalid! accessToken: [%s]", accessToken)
		return "", 0, false
	}

	tokenKey := p.GetAccessTokenKey(accessToken)
	conn := models.GetPassportModel().Get()
	defer conn.Close()
	conn.Send("get", tokenKey)
	conn.Send("ttl", tokenKey)
	conn.Flush()
	var userid string = ""
	var expireSecond int = -1
	if reply, err := redis.String(conn.Receive()); err == nil {
		userid = reply
		if reply, err := redis.Int(conn.Receive()); err == nil {
			expireSecond = reply
		} else {
			log.Warnf("ttl %s error: %s", tokenKey, err.Error())
		}
		return userid, expireSecond, true
	} else {
		log.Warnf("get %s error: %s", tokenKey, err.Error())
	}
	return "", 0, false
}

/*
判断accesstoken和uid是否匹配
*/
func (p *AccessTokenService) IsValidAccessTokenForUserid(accessToken, uid string) bool {
	if "" == accessToken || "" == uid {
		log.Warnf("parsms invalid for accessToken: [%s], uid: [%s]", accessToken, uid)
		return false
	}

	if userid, _, ok := p.GetUseridByAccessToken(accessToken); ok && userid == uid {
		return true
	}
	return false
}

/*
	新生成access_token
*/
func (p *AccessTokenService) GenerateNewAccessToken(akid int) (string, bool) {
	ak := fmt.Sprintf("%s%d", utility.GenerateUUIDBase64(), akid)
	return ak, true
}

/*
	产生并纪录access_token
*/
func (p *AccessTokenService) GenerateAndRecordAccessToken(userid string, akid int) (string, bool) {
	accessToken, ok := p.GenerateNewAccessToken(akid)
	if !ok {
		return "", false
	}

	if err := p.recordAccessToken(accessToken, userid, akid); err != nil {
		log.Errorf("RecordAccessToken error, msg: %s", err.Error())
		return "", false
	}

	return accessToken, true
}
